package com.ajavaer.framework.generator;

import com.ajavaer.framework.common.message.Message;
import com.ajavaer.framework.common.tools.StringTools;
import com.ajavaer.framework.common.validate.Validation;
import com.ajavaer.framework.config.GeneratorConfig;
import com.ajavaer.framework.generator.model.ClassField;
import com.ajavaer.framework.generator.model.ClassInfo;
import com.ajavaer.framework.generator.reader.InfoReader;
import com.ajavaer.framework.generator.reader.PoiWordTools;
import freemarker.template.Configuration;
import freemarker.template.Template;
import org.apache.poi.xwpf.usermodel.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.io.*;
import java.util.List;

import static com.ajavaer.framework.common.tools.StringTools.VIRGULE;

/**
 * 简单代码生成器
 * <p>
 * 目前支持从excel生成po,dao,service,controller<br/>
 * 从mysql生成po,dao,service,controller<br/>
 * 从java po生成dao,service,controller<br/>
 * 生成前请先清理输出目录文件
 * </p>
 */
@Component
@ConditionalOnProperty("ajavaer.generator.out-dir")
public class AJavaerGenerator implements InitializingBean {
    private Logger log = LoggerFactory.getLogger(AJavaerGenerator.class);
    @Resource
    private GeneratorConfig generatorConfig;

    private Template templatePo;
    private Template templateDto;
    private Template templateDtoMapper;
    private Template templateDAO;
    private Template templateService;
    private Template templateImplService;
    private Template templateController;

    private String poDir;
    private String dtoDir;
    private String dtoMapperDir;
    private String daoDir;
    private String serviceDir;
    private String controllerDir;
    @Resource
    private InfoReader infoReader;

    /**
     * 从excel文件生成代码
     * <p>
     * 参考示例 demo.xlsx
     * </p>
     *
     * @param excelFile excel文件
     * @return
     * @throws IOException
     */
    public Message fromExcel(File excelFile) throws IOException {
        List<ClassInfo> classInfos = infoReader.readExcel(excelFile);
        generator(classInfos, true);
        return Message.success("生成完毕");
    }

    /**
     * 从mysql表中生成代码
     *
     * @return
     */
    public Message fromMysql() {
        List<ClassInfo> classInfos = infoReader.readMySQL();
        generator(classInfos, true);
        return Message.success("生成完毕");
    }

    /**
     * 根据po生成dao和service代码
     *
     * @param pos
     * @return
     */
    public Message fromPo(Class<?>... pos) {
        List<ClassInfo> classInfos = infoReader.readPo(pos);
        generator(classInfos, false);
        return Message.success("生成完毕");
    }

    public Message createDataBaseDictionary(List<ClassInfo> classInfos) throws IOException {
        if (classInfos == null) {
            classInfos = infoReader.readMySQL();
        }
        checkOutDir();
        String outDir = generatorConfig.getOutDir();
        XWPFDocument template = new XWPFDocument();
        for (int i = 0; i < classInfos.size(); i++) {
            ClassInfo classInfo = classInfos.get(i);
            XWPFParagraph paragraph = template.createParagraph();
            paragraph.setAlignment(ParagraphAlignment.LEFT);
            paragraph.setStyle("1");
            XWPFRun run = paragraph.createRun();
            run.setText(StringTools.format("{0}({1})", classInfo.getTableName(), classInfo.getClassComment()));
            XWPFTable table = template.createTable(1, 7);
            XWPFTableRow columnTitle = table.getRows().get(0);
            columnTitle.getCell(0).setText("序号");
            PoiWordTools.setCellWidth(columnTitle.getCell(0), 1000);
            columnTitle.getCell(1).setText("字段名称");
            PoiWordTools.setCellWidth(columnTitle.getCell(1), 1500);
            columnTitle.getCell(2).setText("字段描述");
            PoiWordTools.setCellWidth(columnTitle.getCell(2), 2000);
            columnTitle.getCell(3).setText("字段类型");
            PoiWordTools.setCellWidth(columnTitle.getCell(3), 1000);
            columnTitle.getCell(4).setText("长度");
            PoiWordTools.setCellWidth(columnTitle.getCell(4), 1000);
            columnTitle.getCell(5).setText("允许空");
            PoiWordTools.setCellWidth(columnTitle.getCell(5), 1000);
            columnTitle.getCell(6).setText("缺省值");
            PoiWordTools.setCellWidth(columnTitle.getCell(6), 1000);
            List<ClassField> classFields = classInfo.getClassFields();
            for (int j = 0; j < classFields.size(); j++) {
                XWPFTableRow row = table.createRow();
                ClassField classField = classFields.get(j);
                row.getCell(0).setText((j + 1) + "");
                row.getCell(1).setText(classField.getFieldName());
                row.getCell(2).setText(classField.getFieldComment());
                row.getCell(3).setText(classField.getDbType());
                row.getCell(4).setText(classField.getLength() + "");
                row.getCell(5).setText(classField.getNotNull() ? "否" : "是");
                row.getCell(6).setText(classField.getDefaultValue());
            }
            template.createParagraph();
            template.createParagraph();
        }
        OutputStream os = new FileOutputStream(outDir + "\\database-dictionary.docx");
        template.write(os);
        PoiWordTools.close(template);
        return Message.success("生成完毕，试试使用宏命令美化一下吧~\n\n" + "Sub 批量修改表格()\n" +
                " \n" +
                "    Dim tempTable As Table\n" +
                "    Application.ScreenUpdating = False\n" +
                "    If ActiveDocument.ProtectionType = wdAllowOnlyFormFields Then\n" +
                "        MsgBox \"文档已保护，此时不能选中多个表格！\"\n" +
                "        Exit Sub\n" +
                "    End If\n" +
                "    ActiveDocument.DeleteAllEditableRanges wdEditorEveryone\n" +
                "    For Each tempTable In ActiveDocument.Tables\n" +
                "        tempTable.Range.Editors.Add wdEditorEveryone\n" +
                "    Next\n" +
                "    ActiveDocument.SelectAllEditableRanges wdEditorEveryone\n" +
                "    ActiveDocument.DeleteAllEditableRanges wdEditorEveryone\n" +
                "    Application.ScreenUpdating = True\n" +
                "    \n" +
                "End Sub");
    }

    private void generator(List<ClassInfo> classInfos, boolean outPo) {
        for (ClassInfo classInfo : classInfos) {
            classInfo.setPoPackage(generatorConfig.getPoPackage());
            classInfo.setDtoPackage(generatorConfig.getDtoPackage());
            classInfo.setDtoMapperPackage(generatorConfig.getDtoMapperPackage());
            classInfo.setDaoPackage(generatorConfig.getDaoPackage());
            classInfo.setServicePackage(generatorConfig.getServicePackage());
            classInfo.setControllerPackage(generatorConfig.getControllerPackage());
            classInfo.setJpaNamingType(generatorConfig.getJpaNamingType());
            classInfo.process();
            if (outPo) {
                outFile(classInfo, poDir + VIRGULE + classInfo.getClassName() + ".java", templatePo);
                if (!StringTools.isBlank(dtoDir, dtoMapperDir)) {
                    outFile(classInfo, dtoDir + VIRGULE + classInfo.getClassName() + "Dto.java", templateDto);
                    outFile(classInfo, dtoMapperDir + VIRGULE + classInfo.getClassName() + "Mapper.java", templateDtoMapper);
                }
            }
            outFile(classInfo, daoDir + VIRGULE + classInfo.getClassName() + "DAO.java", templateDAO);
            outFile(classInfo, serviceDir + VIRGULE + classInfo.getClassName() + "Service.java", templateService);
            outFile(classInfo, serviceDir + VIRGULE + "impl" + VIRGULE + classInfo.getClassName() + "ServiceImpl.java", templateImplService);
            if (needController()) {
                outFile(classInfo, controllerDir + VIRGULE + classInfo.getClassName() + "Controller.java", templateController);
            }
        }
    }

    private void checkOutDir() {
        Validation.assertSuccess(generatorConfig);
        String outDir = generatorConfig.getOutDir();
        File out = new File(outDir);
        if (!out.exists()) {
            out.mkdirs();
        }
        if (out.list().length > 0) {
            Validation.error("生成目录非空");
        }
        createDir(out);
    }

    private void createDir(File outDir) {
        poDir = outDir + VIRGULE + generatorConfig.getPoPackage().replaceAll("\\.", VIRGULE);
        if (StringTools.isNotBlank(generatorConfig.getDtoPackage())) {
            dtoDir = outDir + VIRGULE + generatorConfig.getDtoPackage().replaceAll("\\.", VIRGULE);
        }
        if (StringTools.isNotBlank(generatorConfig.getDtoMapperPackage())) {
            dtoMapperDir = outDir + VIRGULE + generatorConfig.getDtoMapperPackage().replaceAll("\\.", VIRGULE);
        }
        daoDir = outDir + VIRGULE + generatorConfig.getDaoPackage().replaceAll("\\.", VIRGULE);
        serviceDir = outDir + VIRGULE + generatorConfig.getServicePackage().replaceAll("\\.", VIRGULE);
        new File(poDir).mkdirs();
        new File(dtoDir).mkdirs();
        new File(dtoMapperDir).mkdirs();
        new File(daoDir).mkdirs();
        new File(serviceDir).mkdirs();
        new File(serviceDir + VIRGULE + "impl" + VIRGULE).mkdirs();
        if (needController()) {
            controllerDir = outDir + VIRGULE + generatorConfig.getControllerPackage().replaceAll("\\.", VIRGULE);
            new File(controllerDir).mkdirs();
        }
    }


    private void outFile(ClassInfo classInfo, String outPath, Template template) {
        Validation.assertSuccess(classInfo);
        File outFile = new File(outPath);
        try (Writer out = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(outFile)))) {
            template.process(classInfo, out);
            out.flush();
        } catch (Exception e) {
            log.error(e.getMessage(), e);
        }
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        checkOutDir();
        Configuration configuration = new Configuration();
        File file = new File(generatorConfig.getTemplateDir());
        if (!file.exists()) {
            throw Validation.error("未找到模版目录:" + generatorConfig.getTemplateDir());
        }
        configuration.setDirectoryForTemplateLoading(file);
        templatePo = configuration.getTemplate("po.ftl");
        templateDto = configuration.getTemplate("dto.ftl");
        templateDtoMapper = configuration.getTemplate("dtoMapper.ftl");
        templateDAO = configuration.getTemplate("dao.ftl");
        templateService = configuration.getTemplate("service.ftl");
        templateImplService = configuration.getTemplate("serviceImpl.ftl");
        if (needController()) {
            templateController = configuration.getTemplate("controller.ftl");
        }
    }

    private boolean needController() {
        return StringTools.isNotBlank(generatorConfig.getControllerPackage());
    }
}
